package com.diao.myhub.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.diao.myhub.asserts.ServiceAsserts;
import com.diao.myhub.cache.AllTagCache;
import com.diao.myhub.enums.CommentTypeEnum;
import com.diao.myhub.enums.LikeTypeEnum;
import com.diao.myhub.exception.CustomizeError;
import com.diao.myhub.exception.CustomizeException;
import com.diao.myhub.model.dto.GreatDTO;
import com.diao.myhub.model.dto.QuestionDTO;
import com.diao.myhub.model.param.CommentParam;
import com.diao.myhub.model.param.PageParam;
import com.diao.myhub.model.param.QuestionQueryParam;
import com.diao.myhub.model.po.*;
import com.diao.myhub.model.vo.*;
import com.diao.myhub.service.*;
import jakarta.servlet.http.HttpSession;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import java.util.*;
import java.util.stream.Collectors;

@RestController
@RequiredArgsConstructor
@Slf4j
public class QuestionRestController {

    private final NotifyService notifyService;
    private final QuestionService questionService;
    private final UserService userService;
    private final CommentService commentService;
    private final GreatService greatService;
    private final HttpSession session;
    private final AllTagCache allTagCache;

    @PostMapping("/questions/index")
    public IPage<QuestionVO> index(@RequestBody QuestionQueryParam queryParam) {
        QueryWrapper<Question> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByDesc("gmt_create");
        queryWrapper.lambda().select(Question.class, col -> !Objects.equals(col.getColumn(), "description"));
        queryWrapper.eq(Objects.nonNull(queryParam.getCreator()),
                "creator", queryParam.getCreator());
        queryWrapper.like(StringUtils.hasText(queryParam.getTag()),
                "tag", queryParam.getTag());
        queryWrapper.like(StringUtils.hasText(queryParam.getSearch()),
                "title", queryParam.getSearch());
        IPage<Question> page = new Page<>();
        page.setSize(queryParam.getSize());
        page.setCurrent(queryParam.getPage());
        IPage<Question> questionPage = questionService.page(page, queryWrapper);
        Page<QuestionVO> questionVOPage = Page.of(questionPage.getCurrent(), questionPage.getSize(), questionPage.getTotal());
        List<Question> records = questionPage.getRecords();
        if (!CollectionUtils.isEmpty(records)) {
            Set<Integer> authors = records.stream().map(Question::getCreator)
                    .collect(Collectors.toSet());
            QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
            userQueryWrapper.lambda().in(User::getId, authors);
            Map<Integer, User> userMap = userService.list(userQueryWrapper).stream()
                    .collect(Collectors.toMap(User::getId, user -> user, (k1, k2) -> k1));
            List<QuestionVO> newRecords = records.stream().map(question -> {
                QuestionVO questionVO = new QuestionVO();
                BeanUtils.copyProperties(question, questionVO);
                Optional<User> userOptional = Optional.ofNullable(userMap.get(question.getCreator()));
                String avatar = userOptional.map(User::getAvatarUrl).orElse("");
                String name = userOptional.map(User::getName).orElse("");
                questionVO.setCreatorAvatar(avatar);
                questionVO.setCreatorName(name);
                return questionVO;
            }).toList();
            questionVOPage.setRecords(newRecords);
        }
        return questionVOPage;
    }

    @GetMapping("/questions/{id}")
    public QuestionInfoVO question(@PathVariable("id") Long id, Long notify) {
        QuestionInfoVO questionInfoVO = new QuestionInfoVO();
        User user = (User) session.getAttribute("user");
        Question question = questionService.getById(id);
        ServiceAsserts.notNull(question, "该问题不存在");
        readQuestion(id, notify, user);
        QuestionVO questionVO = new QuestionVO();
        BeanUtils.copyProperties(question, questionVO);
        setLikeStatus(id, user, questionVO);
        setCreatorInfo(user, questionVO);
        questionInfoVO.setDetail(questionVO);
        List<QuestionDTO> related = questionService.getQuestionsRelated(id);
        questionInfoVO.setRelated(related);
        return questionInfoVO;
    }

    @GetMapping("/questions/{id}/detail")
    public Question getQuestionEditInfo(@PathVariable("id") Long id, Long notify) {
        User user = (User) session.getAttribute("user");
        ServiceAsserts.isTrue(user != null, "请先登录");
        Question question = questionService.getById(id);
        ServiceAsserts.notNull(question, "该问题不存在");
        ServiceAsserts.isTrue(Objects.equals(user.getId(), question.getCreator()), "您没有权限编辑该问题");
        return question;
    }

    @PostMapping("/questions/{qid}/comments")
    public CommentPageVO getComments(@PathVariable("qid") Long qid,
                                     @RequestBody PageParam param) {
        Long pid = Optional.ofNullable(param).map(PageParam::getPid).orElse(qid);
        Page<Comment> commentPage = commentService.query()
                .eq("type", param.getType())
                .eq("parent_id", pid)
                .orderBy(true, false, "gmt_create")
                .page(Page.of(param.getCurrent(), param.getSize()));
        List<Comment> comments = commentPage.getRecords();
        CommentPageVO resultPage = CommentPageVO.of(commentPage.getCurrent(), commentPage.getSize(), commentPage.getTotal());
        resultPage.setTotalComments(commentService.query().eq("question_id", qid).count());
        resultPage.setRecords(Collections.emptyList());
        if (!CollectionUtils.isEmpty(comments)) {
            List<Integer> commenters = comments.stream().map(Comment::getCommenter).toList();
            List<Long> commentIds = comments.stream().map(Comment::getId).toList();
            QueryWrapper<Comment> queryWrapper = new QueryWrapper<>();
            queryWrapper
                    .select(" id, COUNT(1) as count ")
                    .eq("type", CommentTypeEnum.COMMENT_TYPE_COMMENT.getType())
                    .in("parent_id", commentIds)
                    .groupBy("parent_id");
            Map<Long, Long> countMap = commentService.listMaps(queryWrapper)
                    .stream().collect(Collectors.toMap(map -> (Long) map.get("id"), map -> (Long) map.get("count"), (m1, m2) -> m1));
            Map<Integer, User> userMap = userService.query()
                    .in("id", commenters)
                    .list().stream()
                    .collect(Collectors.toMap(User::getId, user -> user, (k1, k2) -> k1));
            Map<Long, Short> praiseStatusMap = getCommentsPraiseStatusMap(commentIds);
            List<CommentVO> commentVOList = comments.stream().map(comment -> {
                CommentVO commentVO = new CommentVO();
                BeanUtils.copyProperties(comment, commentVO);
                Optional.of(userMap.get(comment.getCommenter()))
                        .ifPresent(user -> {
                            commentVO.setCommenterAvatar(user.getAvatarUrl());
                            commentVO.setCommenterName(user.getName());
                        });
                Optional.ofNullable(countMap.get(comment.getId())).ifPresent(commentVO::setSubCommentCount);
                Optional.ofNullable(praiseStatusMap.get(comment.getId())).ifPresent(commentVO::setLikedStatus);
                return commentVO;
            }).toList();
            resultPage.setRecords(commentVOList);
        }
        return resultPage;
    }

    @GetMapping(value = "/questions/{id}/praise")
    @Transactional(rollbackFor = RuntimeException.class)
    public String praiseQuestion(@PathVariable Long id, short status) {
        User user = (User) session.getAttribute("user");
        GreatDTO greatDTO = new GreatDTO();
        greatDTO.setLikeId(id);
        greatDTO.setStatus(status);
        greatDTO.setType(LikeTypeEnum.LIKE_TYPE_QUESTION.getType());
        praise(user, greatDTO);
        return "ok";
    }

    @PostMapping("/questions/{id}/comment")
    public String postComment(@RequestBody CommentParam param, @PathVariable Long id) {
        ServiceAsserts.notNull(id, "问题ID不能为空");
        ServiceAsserts.notEmpty(param.getContent(), "评论内容不能为空");
        User user = (User) session.getAttribute("user");
        ServiceAsserts.notNull(user, "用户未登录，无法进行评论");
        Comment comment = new Comment();
        comment.setQuestionId(id);
        comment.setGmtCreate(System.currentTimeMillis());
        comment.setCommenter(user.getId());
        comment.setGmtModify(comment.getGmtCreate());
        comment.setContent(param.getContent());
        comment.setType(param.getType());
        comment.setParentId(param.getParentId());
        commentService.addComment(comment);
        notifyService.addNotify(comment);
        return "ok";
    }

    @GetMapping(value = "/questions/comments/{id}/praise")
    @Transactional(rollbackFor = RuntimeException.class)
    public String praiseComment(@PathVariable Long id, short status) {
        User user = (User) session.getAttribute("user");
        GreatDTO greatDTO = new GreatDTO();
        greatDTO.setLikeId(id);
        greatDTO.setStatus(status);
        greatDTO.setType(LikeTypeEnum.LIKE_TYPE_COMMENT.getType());
        praise(user, greatDTO);
        return "ok";
    }

    @GetMapping("/questions/{id}/delete")
    public String deleteQuestion(@PathVariable("id") Long id, HttpSession session) {
        User user = (User) session.getAttribute("user");
        ServiceAsserts.notNull(user, CustomizeError.NOT_ALLOWED.getMsg());
        Question question = questionService.getQuestion(id);
        ServiceAsserts.notNull(question, CustomizeError.QUESTION_NOT_FOUND.getMsg());
        ServiceAsserts.isTrue(Objects.equals(question.getCreator(), user.getId()), CustomizeError.NOT_ALLOWED.getMsg());
        int res = questionService.delQuestionById(id);
        ServiceAsserts.isTrue(res > 0, CustomizeError.QUESTION_NOT_FOUND.getMsg());
        return "删除成功!";
    }

    @GetMapping("/questions/comment/{cid}/delete")
    public String deleteComment(@PathVariable Long cid) {
        User user = (User) session.getAttribute("user");
        ServiceAsserts.notNull(user, CustomizeError.NOT_ALLOWED.getMsg());
        ServiceAsserts.notNull(cid, "评论不存在");
        Comment comment = commentService.getById(cid);
        ServiceAsserts.notNull(comment, CustomizeError.COMMENT_NOT_FOUND.getMsg());
        ServiceAsserts.isTrue(Objects.equals(comment.getCommenter(), user.getId()), CustomizeError.NOT_ALLOWED.getMsg());
        // 删除当前评论下的次级评论
        if (Objects.equals(CommentTypeEnum.COMMENT_TYPE_QUESTION.getType(), comment.getType())) {
            List<Long> removeList = new ArrayList<>(commentService.list(new QueryWrapper<Comment>()
                    .select("id")
                    .eq("parent_id", cid)
                    .eq("type", CommentTypeEnum.COMMENT_TYPE_COMMENT.getType())
            ).stream().map(Comment::getId).toList());
            removeList.add(cid);
            notifyService.remove(new QueryWrapper<Notify>().in("outer_id", removeList));
            commentService.remove(new QueryWrapper<Comment>().in("id", removeList));
            updateQuestionCommentCount(cid);
        } else if (Objects.equals(CommentTypeEnum.COMMENT_TYPE_COMMENT.getType(), comment.getType())) {
            commentService.remove(new QueryWrapper<Comment>().eq("id", cid));
            notifyService.remove(new QueryWrapper<Notify>().eq("outer_id", cid));
            updateQuestionCommentCount(comment.getParentId());
        } else {
            throw new CustomizeException(CustomizeError.COMMENT_TYPE_ERROR);
        }
        return "ok";
    }

    @PostMapping("/questions/publish/save-or-update")
    public Long doSaveOrUpdate(@RequestBody Question question) {
        ServiceAsserts.notNull(question, CustomizeError.QUESTION_EMPTY);
        ServiceAsserts.notBlank(question.getTitle(), CustomizeError.QUESTION_TITLE_EMPTY);
        ServiceAsserts.notBlank(question.getDescription(), CustomizeError.QUESTION_CONTENT_EMPTY);
        ServiceAsserts.isTrue(validateTags(question.getTag()), CustomizeError.QUESTION_TAG_INVALID);
        User user = (User) session.getAttribute("user");
        ServiceAsserts.notNull(user, CustomizeError.NOT_ALLOWED);
        if (question.getId() != null) {
            validateAuthAndUpdate(question, user);
            return question.getId();
        }
        question.setCreator(user.getId());
        question.setGmtCreate(System.currentTimeMillis());
        question.setGmtModify(question.getGmtCreate());
        questionService.save(question);
        return question.getId();
    }

    @PostMapping("/questions/publish/content-update")
    public String updateContent(@RequestBody Question question) {
        ServiceAsserts.notNull(question, CustomizeError.QUESTION_EMPTY);
        ServiceAsserts.notNull(question.getId(), CustomizeError.QUESTION_NOT_FOUND);
        ServiceAsserts.notBlank(question.getDescription(), CustomizeError.QUESTION_CONTENT_EMPTY);
        User user = (User) session.getAttribute("user");
        ServiceAsserts.notNull(user, CustomizeError.NOT_ALLOWED);
        validateAuthAndUpdate(question, user);
        return "ok";
    }

    private void validateAuthAndUpdate(Question question, User user) {
        Question current = questionService.query()
                .select("id", "creator")
                .eq("id", question.getId()).one();
        ServiceAsserts.notNull(current, CustomizeError.QUESTION_NOT_FOUND);
        ServiceAsserts.isTrue(Objects.equals(user.getId(), current.getCreator()), CustomizeError.NOT_ALLOWED);
        question.setGmtModify(System.currentTimeMillis());
        questionService.updateById(question);
    }


    private void setLikeStatus(Long id, User user, QuestionVO questionVO) {
        if (user != null) {
            Integer uid = user.getId();
            Great great = greatService.query()
                    .eq("user_id", uid)
                    .eq("like_id", id)
                    .eq("type", LikeTypeEnum.LIKE_TYPE_QUESTION.getType())
                    .select().one();
            if (great != null) {
                questionVO.setLikeType(great.getStatus());
            }
        }
    }

    private Map<Long, Short> getCommentsPraiseStatusMap(List<Long> commentIds) {
        User user = (User) session.getAttribute("user");
        if (user == null) {
            return Collections.emptyMap();
        }
        return greatService.query()
                .eq("user_id", user.getId())
                .in("like_id", commentIds)
                .eq("type", LikeTypeEnum.LIKE_TYPE_COMMENT.getType())
                .list().stream()
                .collect(Collectors.toMap(Great::getLikeId, Great::getStatus, (k1, k2) -> k1));
    }

    private void readQuestion(Long qId, Long notifyId, User user) {
        questionService.updateView(qId);
        if (notifyId != null) {
            try {
                notifyService.readOne(notifyId);
                userService.updateUnreadDe(user.getId());
            } catch (Exception e) {
                log.error("read question error:", e);
            }
        }
    }

    private void setCreatorInfo(User user, QuestionVO questionVO) {
        Integer creator = questionVO.getCreator();
        if (user == null || !Objects.equals(user.getId(), creator)) {
            User userInfo = userService.getUserById(creator);
            questionVO.setCreatorAvatar(userInfo.getAvatarUrl());
            questionVO.setCreatorName(userInfo.getName());
        } else {
            questionVO.setCreatorAvatar(user.getAvatarUrl());
            questionVO.setCreatorName(user.getName());
        }
    }

    private void praise(User user, GreatDTO greatDTO) {
        // 如果用户不为空,记录或修改点赞对象和用户的关系
        if (user != null) {
            List<Great> record = greatService.getLikeRecord(user.getId(), greatDTO.getType(), greatDTO.getLikeId());
            Great great = new Great();
            BeanUtils.copyProperties(greatDTO, great);
            great.setUserId(user.getId());
            if (record == null || record.isEmpty()) {
                greatService.createRecord(great);
            } else {
                // 刷新数据库记录当中的点赞状态
                record.get(0).setStatus(greatDTO.getStatus());
                greatService.updateRecord(record.get(0));
            }
            // 刷新用户表和评论表点赞记录
            if (LikeTypeEnum.LIKE_TYPE_QUESTION.getType() == greatDTO.getType()) {
                questionService.refreshLikeCount(greatDTO.getLikeId());
            } else if (LikeTypeEnum.LIKE_TYPE_COMMENT.getType() == greatDTO.getType()) {
                commentService.refreshLikeCount(greatDTO.getLikeId());
            }
        }
    }

    public void updateQuestionCommentCount(Long qid) {
        questionService.update()
                .eq("id", qid)
                .set("comment_count", commentService.query()
                        .eq("question_id", qid).count());
    }

    private boolean validateTags(String inputTag) {
        if (StringUtils.hasText(inputTag)) {
            Set<String> allTags = allTagCache.getAllTags();
            for (String tag : inputTag.split(",")) {
                if (!allTags.contains(tag)) {
                    return false;
                }
            }

        }
        return true;
    }
}
